#pragma once

#include<iostream>
#include<string>
#include<queue>
#include<pthread.h>
using namespace std;

template<typename T>
class BlockQueue
{
public:
    //构造
    BlockQueue(){};//默认构造
    BlockQueue(int cap):_cap(cap)
    {
        _productor_wait_num = 0;
        _consumer_wait_num = 0;
        //为锁、条件变量初始化
        pthread_mutex_init(&_mutex , nullptr);
        pthread_cond_init(&_product_cond,nullptr);
        pthread_cond_init(&_consum_cond , nullptr);
    }
    //判空
    bool IsEmpty()
    {
        return _pq.empty();
    }
    //判满
    bool IsFull()
    {
        return _pq.size() == _cap;
    }

    //生产
    void Enqueue(T& in)
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        //只要没满就将东西放入
        while(IsFull())
        {
            _productor_wait_num++;//等待的数量
            //满了就进行等待
            pthread_cond_wait(&_product_cond , &_mutex);//唤醒之后会继续向下走
            _productor_wait_num--;
        }
        //到了这个地方就是没满，可以向其中放入
        _pq.push(in);
        //当有消费者在排队的时候可以通知消费者来消费
        if(_consumer_wait_num>0) 
            pthread_cond_signal(&_consum_cond);//唤醒
        pthread_mutex_unlock(&_mutex);//解锁

    }

    //消费者进行消费
    void Pop(T* out)
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            //是空的，消费者就需要排队等待，并且统计等待的消费者的数量
            _consumer_wait_num++;
            pthread_cond_wait(&_consum_cond,&_mutex);
            _consumer_wait_num--;
        }
        //走到这个地方就代表不为空，消费者可以进行消费
        *out = _pq.front();
        _pq.pop();
        //如果有生产者排队，可以唤醒生产者
        if(_productor_wait_num>0)
            pthread_cond_signal(&_product_cond);
        pthread_mutex_unlock(&_mutex);//解锁
    }

    //析构
    ~BlockQueue()
    {
        //为锁、条件变量调用销毁
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_product_cond);
        pthread_cond_destroy(&_consum_cond);

    }
private:
    std::queue<T> _pq;//阻塞队列
    int _cap;//上限
    pthread_mutex_t _mutex;//锁

    pthread_cond_t _product_cond;//给生产者的条件变量
    pthread_cond_t _consum_cond;//给消费者的条件变量

    int _productor_wait_num;
    int _consumer_wait_num;
};